SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建 您所在的位置:网站首页 securitymanager security systemgetsecuritymanager SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建

SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建

2023-04-08 15:54| 来源: 网络整理| 查看: 265

技术栈 : SpringBoot + shiro + jpa + freemark ,因为篇幅原因,这里只贴了部分代码说明,完整项目地址 : https://github.com/EalenXie/shiro-rbac-system

1 . 新建一个项目名为shiro-rbac-system,pom.xml加入基本依赖 :

4.0.0 org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE name.ealen shiro-rbac-system 0.0.1 shiro-rbac-system SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建 1.8 EalenXie SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计 org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-freemarker mysql mysql-connector-java runtime org.springframework.boot spring-boot-starter-test test org.apache.shiro shiro-spring 1.3.2 com.alibaba fastjson 1.2.31 org.springframework.boot spring-boot-maven-plugin

基本的数据对象关系 :

一个用户对应一个或者多个角色。 一个角色对应一个或者多个权限。 一个权限对应能够访问对应的API或url资源。

1 . RBAC基本实体关系,Permission类(权限资源) :

/** * Created by EalenXie on 2019/3/25 11:15. *

* 权限许可(Permission) 操作 及其能访问url 权限对应一个url地址 */ @Entity @Table(name = "system_shiro_permission") public class Permission extends BaseEntity { @Column(unique = true) private String name; //权限名 唯一 @Column(unique = true) private String url; //访问地址信息 唯一 private String description; //描述信息 //省略getter/setter }

2 . Role类(用户角色),一个角色拥有一个或者多个权限 :

/** * Created by EalenXie on 2019/3/25 11:18. *

* 角色(Role) 角色下面对应多个权限 */ @Entity @Table(name = "system_shiro_role") public class Role extends BaseEntity { @Column(unique = true) private String name; //角色名 唯一 private String description; //描述信息 @ManyToMany(fetch= FetchType.EAGER) private List permissions; //一个用户角色对应多个权限 //省略getter/setter }

3 . User类(用户),一个用户拥有一个或者多个角色 :

/** * Created by EalenXie on 2019/3/25 11:01. *

* 用户表(User) 用户下面对应多个角色 */ @Entity @Table(name = "system_shiro_user") public class User extends BaseEntity { @Column(unique = true) private String username;//用户名 唯一 private String password;//用户密码 private String passwordSalt;//用户密码加密盐值 @ManyToMany(fetch = FetchType.EAGER) private List roles;//用户角色 一个用户可能有一个角色,也可能有 多个角色 //省略getter/setter }

2 . 基本配置信息 : 1 . 配置应用的基本信息,application.yml :

server: port: 8082 spring: application: name: shiro-rbac-system resources: static-locations: classpath:/ freemarker: template-loader-path: classpath:/templates/ suffix: .html content-type: text/html charset: UTF-8 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/yourdatabase?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8&useSSL=true username: yourname password: yourpass jpa: # show-sql: true hibernate: ddl-auto: update open-in-view: false # 禁用 OSIV http: encoding: charset: utf-8 enabled: true

2 . 配置JPA,基本的数据库访问Dao。

public interface PermissionRepository extends JpaRepository { Permission findByName(String name); } public interface RoleRepository extends JpaRepository { Role findByName(String name); } public interface UserRepository extends JpaRepository { User findByUsername(String username); }

3 . 配置shiro,基于角色访问控制权限的核心Realm,UserAuthRealm :

@Component public class UserAuthRealm extends AuthorizingRealm { @Resource private UserRepository userRepository; /** * 权限核心配置 根据数据库中的该用户 角色 和 权限 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); User user = (User) principals.getPrimaryPrincipal(); for (Role role : user.getRoles()) { //获取 角色 authorizationInfo.addRole(role.getName()); //添加 角色 for (Permission permission : role.getPermissions()) { //获取 权限 authorizationInfo.addStringPermission(permission.getName());//添加 权限 } } return authorizationInfo; } /** * 用户登陆 凭证信息 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String) token.getPrincipal(); User user = userRepository.findByUsername(username); if (user == null) return null; String credentials = user.getPasswordSalt() + user.getUsername() + user.getPasswordSalt();//自定义加盐 salt + username + salt return new SimpleAuthenticationInfo( user, //用户名 user.getPassword(), //密码 ByteSource.Util.bytes(credentials), //加密 getName() //realm name ); } /** * 设置 realm的 HashedCredentialsMatcher */ @PostConstruct public void setHashedCredentialsMatcher() { this.setCredentialsMatcher(hashedCredentialsMatcher()); } /** * 凭证匹配 : 指定 加密算法,散列次数 */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法; hashedCredentialsMatcher.setHashIterations(1024);//散列的次数,比如散列两次,相当于 md5(md5("")); return hashedCredentialsMatcher; } }

4 . shiro核心配置,包含基本过滤器策略,注解支持等。

/** * Created by EalenXie on 2019/3/25 15:12. */ @Configuration public class ShiroConfig { @Resource private PermissionRepository permissionRepository; @Resource private UserAuthRealm userAuthRealm; /** * 配置 资源访问策略 . web应用程序 shiro核心过滤器配置 */ @Bean public ShiroFilterFactoryBean factoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); factoryBean.setSecurityManager(securityManager); factoryBean.setLoginUrl("/login");//登录页 factoryBean.setSuccessUrl("/index");//首页 factoryBean.setUnauthorizedUrl("/unauthorized");//未授权界面; factoryBean.setFilterChainDefinitionMap(setFilterChainDefinitionMap()); //配置 拦截过滤器链 return factoryBean; } /** * 配置 SecurityManager,可配置一个或多个realm */ @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(userAuthRealm); // securityManager.setRealm(xxxxRealm); return securityManager; } /** * 开启shiro 注解支持. 使以下注解能够生效 : * 需要认证 {@link org.apache.shiro.authz.annotation.RequiresAuthentication RequiresAuthentication} * 需要用户 {@link org.apache.shiro.authz.annotation.RequiresUser RequiresUser} * 需要访客 {@link org.apache.shiro.authz.annotation.RequiresGuest RequiresGuest} * 需要角色 {@link org.apache.shiro.authz.annotation.RequiresRoles RequiresRoles} * 需要权限 {@link org.apache.shiro.authz.annotation.RequiresPermissions RequiresPermissions} */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } /** * 配置 拦截过滤器链. map的键 : 资源地址 ; map的值 : 所有默认Shiro过滤器实例名 * 默认Shiro过滤器实例 参考 : {@link org.apache.shiro.web.filter.mgt.DefaultFilter} */ private Map setFilterChainDefinitionMap() { Map filterMap = new LinkedHashMap(); //注册 数据库中所有的权限 及其对应url List allPermission = permissionRepository.findAll();//数据库中查询所有权限 for (Permission p : allPermission) { filterMap.put(p.getUrl(), "perms[" + p.getName() + "]"); //拦截器中注册所有的权限 } filterMap.put("/static/**", "anon"); //公开访问的资源 filterMap.put("/open/api/**", "anon"); //公开接口地址 filterMap.put("/logout", "logout"); //配置登出页,shiro已经帮我们实现了跳转 filterMap.put("/**", "authc"); //所有资源都需要经过验证 return filterMap; } }

5 . 用于授权相关注解进行测试的API RestController。AuthorizationApiFacade :

/** * Created by EalenXie on 2019/3/26 16:46. * 授权相关API 测试 */ @RestController public class AuthorizationApiFacade { /** * 需要 验证 才能访问的api */ @RequestMapping("/requiresAuthentication") @RequiresAuthentication public Map requiresAuthentication() { Map result = new HashMap(); result.put("msg", "Require Authentication : 需要认证 测试, 能够访问此接口"); return result; } /** * 需要 用户 身份才能访问的api */ @RequiresUser @RequestMapping("/requiresUser") public Map requiresUser() { Map result = new HashMap(); result.put("msg", "Require User : 需要用户 测试, 能够访问此接口"); return result; } /** * 需要 Guest 身份才能访问的api */ @RequiresGuest @RequestMapping("/requiresGuest") public Map requiresGuest() { Map result = new HashMap(); result.put("msg", "Require Guest : 需要认证 测试, 能够访问此接口"); return result; } /** * 需要 administrator 角色才能访问的api */ @RequiresRoles("administrator") @RequestMapping("/requiresRoles") public Map requiresRoles() { Map result = new HashMap(); result.put("msg", "require Roles : 该用户具有 administrator 角色, 能够访问此接口"); return result; } /** * 需要 add 权限才能访问的api */ @RequiresPermissions("add") @RequestMapping("/requiresPermissionsAdd") public Map requiresPermissionsAdd() { Map result = new HashMap(); result.put("msg", "require Permissions : 该用户具有 add 权限 , 能够访问此接口"); return result; } /** * 需要 delete 权限才能访问的api */ @RequiresPermissions("delete") @RequestMapping("/requiresPermissionsDelete") public Map requiresPermissionsDelete() { Map result = new HashMap(); result.put("msg", "require Permissions : 该用户具有 delete 权限 , 能够访问此接口"); return result; } /** * 公开接口 */ @RequestMapping(value = "/open/api/sayHello", method = RequestMethod.POST) public Map sayHello() { Map result = new HashMap(); result.put("msg", "这个是公开接口,谁都可以访问"); return result; } }

进行测试,用户登陆(zhangsan)测试,zhangsan只具有user角色,只有部分权限。

登陆成功后,进入到首页。

访问,/add 则跳转到 新增页面

访问/delete,因为没有权限会跳转到未授权页面。

zhangsan只能调用自己拥有角色和权限的api : 没有相关角色和权限的api不能调用 :


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有